#include <ESP8266HTTPClient.h>

#include <ESP8266WiFi.h>
#include <PubSubClient.h>
#include <time.h>
#include <EEPROM.h>
#include <ArduinoJson.h> 

#include <Wire.h>
#include <TM1650.h>
#include <math.h>
#include <WiFiUdp.h>
#include <NTPClient.h>

TM1650 tm1650;

#define EEPROM_SIZE 512       // 根据实际需要调整EEPROM大小
#define H1_ADDRESS 0          // h1变量的存储地址


// 传感器引脚配置
#define TRIGGER_PIN 14
#define ECHO_PIN    12
#define BUZZER      13 //D7位置
const int delayTime = 1000;           // 每次测量的间隔时间（毫秒）
float Time_Echo_us = 0;       // 声波往返时间（微秒）
float Len_cm = 0;             // 距离（毫米）

unsigned long startTime = 0;  // 记录开始检测的时间
bool isDetecting = false;     // 检测状态标志


//void IRAM_ATTR buttonISR();
volatile bool measureFlag = true;
float h1 = 0;

// WiFi credentials
const char *ssid = "你的WIFI";             
const char *password = "WIFI密码";   

// MQTT Broker settings
const int mqtt_port = 8883;
const char *mqtt_broker = "你的MQTT服务器地址-hangzhou.emqxsl.cn";
const char *gas_topic = "height";          // 新增气体检测主题
const char *mqtt_username = "MQTT账号";  
const char *mqtt_password = "MQTT密码";
String heightTopic = "home/sensor/height/state"; // 计数专用主题


// NTP Server settings

const char* ntp_server = "cn.pool.ntp.org"; // Recommended NTP server for users in China    
const long gmt_offset_sec = 0;            
const int daylight_offset_sec = 0;        

// 为了显示时间NTP客户端配置
WiFiUDP ntpUDP;
NTPClient timeClient(ntpUDP, "pool.ntp.org", 8 * 3600, 60000); // 东八区(UTC+8)
// 时间格式化缓冲区
char timeBuffer[6] = {'\0'}; // 格式"HH:MM" + '\0'

BearSSL::WiFiClientSecure espClient;
PubSubClient mqtt_client(espClient);

// 证书配置保持不变...
static const char ca_cert[] PROGMEM = R"EOF(
-----BEGIN CERTIFICATE-----
MIIDrzCCApegAwIBAgIQCDvgVpBCRrGhdWrJWZHHSjANBgkqhkiG9w0BAQUFADBh
MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBD
QTAeFw0wNjExMTA你的密钥QNVbRTLtm8KPiGxvDl7I90VUw
DQYJKoZIhvcNAQEFBQADggEBAMucN6pIExIK+t1EnE9SsPTfrgT1eXkIoyQY/Esr
hMAtudXH/vTBH1jLuG2cenTnmCmrEbXjcKChzUyImZOMkXDiqw8cvpOp/2PV5Adg
06O/nVsJ8dWO41P0jmP6P6fbtGbfYmbW0W5BjfIttep3Sp+dWOIrWcBAI+0tKIJF
PnlUkiaY4IBIqDfv8NZ5YBberOgOzW6sRBc4L0na4UU+Krk2U886UAb3LujEV0ls
YSEY1QSteDwsOoBrp+uvFRTp2InBuThs4pFsiv9kuXclVzDAGySj4dzp30d8tbQk
CAUw7C29C79Fv1C5qfPrmAESrciIxpg0X40KPMbp1ZWVbd4=
-----END CERTIFICATE-----
)EOF";

// Function declarations
void connectToWiFi();

void connectToMQTT();

void syncTime();


void mqttCallback(char *topic, byte *payload, unsigned int length) {
  String topicStr = String(topic);
  Serial.println(topicStr); // 打印实际接收的主题
  // 处理h1设置命令
  if (topicStr == "home/sensor/height/set") {
    char payloadStr[length + 1];
    memcpy(payloadStr, payload, length);
    payloadStr[length] = '\0';
    
    float newH1 = atof(payloadStr);
    if (newH1 >= 0 && newH1 <= 999.9) {
      h1 = newH1;
      EEPROM.put(H1_ADDRESS, h1);
      if (EEPROM.commit()) {
        Serial.printf("h1 updated to: %.1f\n", h1);
        mqtt_client.publish("home/sensor/height/state", String(h1).c_str());
      }
    }
  }
  

}



//超声波测量函数
float measureDistance() {
  digitalWrite(TRIGGER_PIN, LOW);
  delayMicroseconds(2);
  digitalWrite(TRIGGER_PIN, HIGH);
  delayMicroseconds(11);
  digitalWrite(TRIGGER_PIN, LOW);
  
  Time_Echo_us = pulseIn(ECHO_PIN, HIGH);//获取传播时间
  if (Time_Echo_us >= 1 && Time_Echo_us <= 30000) { // 有效距离范围1us~60000us（约2mm~10m）
      Len_cm = (Time_Echo_us * 343) / 20000;         // 修正后的计算公式（毫米）
      return (float)Len_cm;         // 返回浮点型距离值
  } else {
      Serial.println("Error: Out of range");
      return -1.0;                  // 错误时返回-1
  }
}



//定义一个显示文字的方法
void displayStringOnTM1650(char* value) {

    tm1650.displayOff();
    tm1650.setBrightness(TM1650_MIN_BRIGHT);
    tm1650.displayOn();
    delay(100);

    tm1650.displayString(value);
    delay(5000);
    tm1650.displayOff();
}


//定义一个方法用于显示超声波的数据按照cm单位显示，保留一位小数
void displayFloatOnTM1650(float value) {
    static char buffer[5];
    long integer = round(value * 10);
    sprintf(buffer, "%4ld", integer); // 指定最小宽度为4，不足时左补空格

    tm1650.displayOff();
    tm1650.setBrightness(TM1650_MIN_BRIGHT);
    tm1650.displayOn();
    delay(100);
    buffer[2] |=128;//设置小数点位置
    tm1650.displayString(buffer);
    delay(20000);
    tm1650.displayOff();

}

//显示时间的方法
void displayTimeTM1650() {
    static char timeBuffer[5];
    timeClient.update(); // 更新时间
      // 获取小时和分钟（自动处理24小时制）
    int hours = timeClient.getHours();
    int minutes = timeClient.getMinutes();
      // 格式化为两位数字的字符串
    sprintf(timeBuffer, "%02d%02d", hours, minutes);
    //Serial.printf(timeBuffer);


    tm1650.displayOff();
    tm1650.setBrightness(TM1650_MIN_BRIGHT);
    tm1650.displayOn();
    delay(100);
    timeBuffer[1] |=128;//设置小数点位置
    tm1650.displayString(timeBuffer);
    delay(1000);
    tm1650.displayOff();
    
}

void performMeasurement(){
  float total = 0.0;
  int validCount = 0;

  for (int i = 0; i < 2; i++) {     // 连续测量2次
      float dist = measureDistance();
      if (dist >= 0) {              // 仅累加有效数据
          total += dist;
          validCount++;
      }
      delay(240);             // 每次测量后等待
  }

  if (validCount > 0) {
      float average = total / validCount;
      float RealHeight=h1-average;//这里的200为安装好后设置的数据
      
      String message = "{\"Height\":" + String(RealHeight)+"}";//定义发送给HA消息
      mqtt_client.publish(heightTopic.c_str(), message.c_str());//发送MQTT消息
      //POST_data(message.c_str());
      Serial.print("Average distance: ");
      Serial.print(RealHeight);
      Serial.println(" cm");
      displayFloatOnTM1650(RealHeight);//数码管显示身高数据
  } else {
      Serial.println("All measurements invalid");    
  }

}



void setup() {
  Wire.begin(); //Join the bus as master 数码管初始化
  tm1650.init();

  Serial.begin(9600);
  //超声波初始化
  pinMode(TRIGGER_PIN, OUTPUT);
  pinMode(ECHO_PIN, INPUT);
  //蜂鸣器初始化
  pinMode(BUZZER,OUTPUT);
  digitalWrite(BUZZER,HIGH);
  delay(500);
  digitalWrite(BUZZER,LOW);

  //wifi初始化
  connectToWiFi();
  displayStringOnTM1650("ff");
  syncTime();
  mqtt_client.setServer(mqtt_broker, mqtt_port);
  connectToMQTT();
  displayStringOnTM1650("mqtt");
  timeClient.begin();
  timeClient.update(); // 初始化时间

  mqtt_client.setCallback(mqttCallback);

  //EEPROM初始化，用于存储初始化数据
  // EEPROM初始化，用于存储h1数据
  EEPROM.begin(EEPROM_SIZE);
  // 从EEPROM读取h1
  EEPROM.get(H1_ADDRESS, h1);
  // 验证数据有效性
  if (isnan(h1) || h1 <= 0) { 
    h1 = 200.0; // 默认值
    EEPROM.put(H1_ADDRESS, h1);
    EEPROM.commit();
  }
  Serial.printf("Initial h1 value: %.1f\n", h1);
}

void loop() {
  if (!mqtt_client.connected()) {
    connectToMQTT();
  }
  mqtt_client.loop();

  float currentDistance = measureDistance();
  
  //自动化测量检测方法距离值3s内持续大于150cm则开始测量
  if (currentDistance < 50) {        // 检测到高度大于阈值
    if (!isDetecting) {               // 如果是首次检测到
      startTime = millis();           // 记录开始时间
      isDetecting = true;             // 进入检测状态
      Serial.println("开始检测高度维持状态...");
    } else {                          // 已经在检测状态
      // 检查持续时间是否超过3秒
      if (millis() - startTime >= 3000) {
        digitalWrite(BUZZER,HIGH);
        delay(500);
        digitalWrite(BUZZER,LOW);
        delay(500);
        digitalWrite(BUZZER,HIGH);
        delay(500);
        digitalWrite(BUZZER,LOW);
        performMeasurement();         // 执行身高测量
        isDetecting = false;          // 重置检测状态
        Serial.println("测量完成，等待下次触发");
      }
    }
  } else {                            // 检测到高度未达阈值
    if (isDetecting) {                // 如果之前处于检测状态
      isDetecting = false;            // 重置检测状态
      Serial.println("高度不足，重置检测状态");
    }
  }





  displayTimeTM1650();//显示时间

}




void connectToWiFi() {
    WiFi.begin(ssid, password);
    while (WiFi.status() != WL_CONNECTED) {
        delay(1000);
        Serial.println("Connecting to WiFi...");
    }
    Serial.println("Connected to WiFi");
}

void syncTime() {
    configTime(gmt_offset_sec, daylight_offset_sec, ntp_server);
    Serial.print("Waiting for NTP time sync: ");
    while (time(nullptr) < 8 * 3600 * 2) {
        delay(1000);
        Serial.print(".");
    }
    Serial.println("Time synchronized");
    struct tm timeinfo;
    if (getLocalTime(&timeinfo)) {
        Serial.print("Current time: ");
        Serial.println(asctime(&timeinfo));
    } else {
        Serial.println("Failed to obtain local time");
    }
}
// 以下原有函数保持不变，只需添加订阅...
void connectToMQTT() {
    BearSSL::X509List serverTrustedCA(ca_cert);
    espClient.setTrustAnchors(&serverTrustedCA);
    while (!mqtt_client.connected()) {
        String client_id = "esp8266-client2-" + String(WiFi.macAddress());
        Serial.printf("Connecting to MQTT Broker as %s.....\n", client_id.c_str());
        if (mqtt_client.connect(client_id.c_str(), mqtt_username, mqtt_password)) {
            Serial.println("Connected to MQTT broker");
            mqtt_client.subscribe("home/sensor/height/set"); // 新增订阅

            mqtt_client.publish("height/status", "online");//发布身高主题
            
            //mqtt_client.publish(mqtt_topic, "Hi EMQX I'm ESP8266 ^^");
        } else {
            char err_buf[128];
            espClient.getLastSSLError(err_buf, sizeof(err_buf));
            Serial.print("Failed to connect to MQTT broker, rc=");
            Serial.println(mqtt_client.state());
            Serial.print("SSL error: ");
            Serial.println(err_buf);
            delay(5000);
        }
    }
}



